#pragma once
#include <EASTL/shared_ptr.h>
#include <EASTL/unique_ptr.h>
#include <utl/allocator.h>
#include <common_header.h>

//namespace utl
//{
//	using std::unique_ptr;
//	using std::shared_ptr;
//	using std::make_unique;
//	using std::make_shared;
//}

namespace utl
{
	namespace _internal
	{
		void* utl_mem_alloc(size_t size);
		void utl_free(void* ptr);

		class mem_allocator
		{
		public:
			mem_allocator(const char* name = nullptr)
			{}
			~mem_allocator() = default;
			mem_allocator(const allocator& x)
			{};
			mem_allocator(const allocator& x, const char* pName)
			{};
			mem_allocator& operator=(const mem_allocator& x) = default;

			void* allocate(size_t n, int flags = 0)
			{
				return utl_mem_alloc(n);
			}
			void* allocate(size_t n, size_t alignment, size_t offset, int flags = 0)
			{
				return utl_mem_alloc(n);
			}
			void  deallocate(void* p, size_t n)
			{
				utl_free(p);
			}

			const char* get_name() const
			{
				return nullptr;
			}
			void        set_name(const char* pName)
			{}
		private:

		};

		template <typename T>
		struct default_delete
		{
			constexpr default_delete() noexcept = default;

			template <typename U>
			default_delete(const default_delete<U>&, typename eastl::enable_if<eastl::is_convertible<U*, T*>::value>::type* = 0) noexcept
			{}

			void operator()(T* p) const noexcept
			{
				static_assert(eastl::internal::is_complete_type_v<T>, "Attempting to call the destructor of an incomplete type");
				p->~T();
				utl_free(p);
			}
		};


	}

	using eastl::shared_ptr;

	template<typename T>
	using unique_ptr = eastl::unique_ptr<T, _internal::default_delete<T>>;

	template <typename T, typename... Args>
	eastl::shared_ptr<T> make_shared(Args&&... args)
	{
		return eastl::allocate_shared<T>(_internal::mem_allocator{}, eastl::forward<Args>(args)...);
	}

	template <typename T>
	inline utl::unique_ptr<T> make_unique()
	{
		auto null_ptr = _internal::utl_mem_alloc(sizeof(T));
		T* ptr = ::new(null_ptr) T();
		return utl::unique_ptr<T>(ptr);
	}

	template <typename T, typename... Args>
	inline utl::unique_ptr<T> make_unique(Args&&... args)
	{
		auto null_ptr = _internal::utl_mem_alloc(sizeof(T));
		T* ptr = ::new(null_ptr) T(eastl::forward<Args>(args)...);
		return utl::unique_ptr<T>(ptr);
	}

	template <typename T, typename... Args>
	T* make_pointer(Args&&...args)
	{
		auto null_ptr = _internal::utl_mem_alloc(sizeof(T));
		return (new(null_ptr) T(std::forward<Args>(args)...));
	}

	template <typename T>
	T* make_pointer()
	{
		auto null_ptr = _internal::utl_mem_alloc(sizeof(T));
		return (new(null_ptr) T);
	}

	template<typename T>
	void free_pointer(T* ptr)
	{
		ptr->~T();
		utl_free(ptr);
	}

	/*template <typename T, typename... Args>
	T* make_pointer(Args&&...args)
	{
		auto null_ptr = _internal::mem_allocator.allocate(sizeof(T));
		return (new(null_ptr) T(std::forward<Args>(args)...));
	}

	template <typename T>
	T* make_pointer()
	{
		auto null_ptr = _internal::mem_allocator.allocate(sizeof(T));
		return (new(null_ptr) T);
	}

	template<typename T>
	void free_pointer(T* ptr)
	{
		ptr->~T();
		_internal::mem_allocator.deallocate(ptr, 0);
	}

	template<typename T>
	T* malloc_array(unsigned int count)
	{
		return (T*)_internal::mem_allocator.allocate(sizeof(T) * count);
	}

	inline void release_array(void* ptr)
	{
		_internal::mem_allocator.deallocate(ptr);
	}

	template <typename T, typename... Args>
	T* re_make_pointer(void* null_ptr, Args&&...args)
	{
		return (new(null_ptr) T(std::forward<Args>(args)...));
	}

	template <typename T>
	T* re_make_pointer(void* null_ptr)
	{
		return (new(null_ptr) T);
	}*/

	/*

	template <typename T, typename... Args>
	T* RE_CREATE(T* src,Args&&...args)
	{
		return (new(src) T(std::forward<Args>(args)...));
	}

	template <typename T, typename... Args>
	T* RELEASE_CREATE(T* src, Args&&...args)
	{
		src->~T();
		return (new(src) T(std::forward<Args>(args)...));
	}

	template<typename T>
	void DELETE(T* ptr)
	{
		ptr->~T();
		_internal::mem_allocator.deallocate(ptr, 0);
	}*/
}